import { useEffect, useRef, useState } from "react";
import { useDrag, DndProvider, useDragLayer, useDrop } from "react-dnd";
import { getEmptyImage, HTML5Backend } from "react-dnd-html5-backend";
import "./Dnd.scss";
import { TouchBackend } from "react-dnd-touch-backend";
import { isMobile } from "react-device-detect";
import classNames from "classnames";

interface ItemType {
	color: string;
}
interface BoxProps {
	color: string;
}
/* 
Box 组件负责展示一个带颜色的方块，并且支持拖拽操作。
useDrag: 这是 react-dnd 中的钩子，用来让 Box 变得可拖拽。
- type: "box"：定义了拖拽的类型，所有具有这个类型的元素都可以被放入相应的 drop 区域（Container）。
- item：定义了拖拽过程中传递的对象，这里包含了 color 属性，也就是当前 Box 的颜色。
- collect：这是一个用于从 monitor 中收集数据的函数，比如当前元素是否正在被拖拽 (isDragging)。

*/
function Box(props: BoxProps) {
	const ref = useRef(null);

	const [{ dragging }, drag, dragPreview] = useDrag({
		type: "box",
		item: {
			color: props.color,
		},
		collect(monitor) {
			return {
				dragging: monitor.isDragging(),
			};
		},
	});
	// useEffect: 在 useEffect 中我们调用了 drag 将 ref 关联到拖拽操作上，
	// 并调用 dragPreview(getEmptyImage()) 来隐藏默认的拖拽预览图像，允许我们使用自定义的 DragLayer 作为预览。
	useEffect(() => {
		drag(ref);
		dragPreview(getEmptyImage(), { captureDraggingState: false });
	}, []);

	return (
		<div
			ref={ref}
			className={dragging ? "box dragging" : "box"}
			style={{ background: props.color || "blue" }}
		></div>
	);
}

/* 
Container 是一个可以接收拖拽的区域，并且负责将拖拽的 Box 添加到自己的内部。
useDrop: 通过这个钩子，Container 可以成为一个拖拽的放置目标。
- accept: "box"：定义了该容器可以接受类型为 "box" 的拖拽对象。
- drop：当有一个 Box 放入 Container 时，drop 函数会被调用，item 是从 Box 传递过来的拖拽对象（这里是颜色信息）。
*/
function Container() {
	// boxes 是一个存储 Box 颜色的数组，每当有新的 Box 放入容器时，它会被添加到 boxes 中，并在页面上渲染。
	const [boxes, setBoxes] = useState<ItemType[]>([]);

	const ref = useRef<HTMLDivElement>(null);

	const [, drop] = useDrop({
		accept: "box",
		drop(item: ItemType) {
			setBoxes((boxes) => [...boxes, item]);
		},
	});

	useEffect(() => {
		// 仅在组件挂载时调用一次，将 drop 和 ref 关联
		if (ref.current) {
			drop(ref.current);
		}
	}, [drop]);

	return (
		<div ref={ref} className="container">
			{boxes.map((item, index) => {
				return <Box key={index} color={item.color} />;
			})}
		</div>
	);
}

/* 
DragLayer 是一个可选的高级功能，用来定制拖拽时的预览图像。它可以实时跟踪拖拽中的元素的位置。
- useDragLayer: 用来获取拖拽的状态和拖拽对象的位置信息。
    - isDragging：当前是否正在拖拽。
    - item：当前拖拽的对象（来自 Box 的 color）。
    - currentOffset：拖拽对象的当前位置（用于定位拖拽预览层）。
*/

const DragLayer = () => {
	const { isDragging, item, currentOffset } = useDragLayer((monitor) => ({
		item: monitor.getItem(),
		isDragging: monitor.isDragging(),
		currentOffset: monitor.getSourceClientOffset(),
	}));

	if (!isDragging || !currentOffset) {
		return null;
	}

	return (
		<div
			className="drag-layer"
			style={{
				left: currentOffset?.x,
				top: currentOffset?.y,
				position: "fixed", //  保证拖拽层相对于页面固定
				pointerEvents: "none", //避免拖拽层阻挡鼠标事件
			}}
		>
			{item.color}拖拽中...
		</div>
	);
};

export default function Dnd() {
	return (
		<div className={classNames("columnContainer", "jcCenter", "aiCenter")} style={{marginTop:"24px"}}>
			<DndProvider backend={isMobile ? TouchBackend : HTML5Backend}>
				<Container></Container>
				<div className={classNames("flexContainer", "jcCenter", "aiCenter")}>
					<Box color="blue"></Box>
					<Box color="red"></Box>
					<Box color="green"></Box>
				</div>

				<DragLayer></DragLayer>
			</DndProvider>
		</div>
	);
}
